﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Routing;
using SimpleRest.Serialization;

namespace SimpleRest.Core.Extensions.Routes
{
    public class SampleResponseHttpHandler : IHttpHandler
    {
        public readonly Configuration Configuration;
        public readonly RequestContext RequestContext;
        private readonly Type ReturnType;
        private readonly Type ServiceType;
        public Pipeline OperationPipeline { get; private set; }

        public SampleResponseHttpHandler(Pipeline operationPipeline, Type serviceType, Type returnType, Configuration configuration, RequestContext requestContext)
        {
            this.ReturnType = returnType;
            this.OperationPipeline = operationPipeline;
            this.Configuration = configuration;
            this.RequestContext = requestContext;
            this.ServiceType = serviceType;
        }

        public bool IsReusable
        {
            get { return false; }
        }

        public void ProcessRequest(HttpContext context)
        {
            var request = new HttpRequestMessage(this.RequestContext);
            var response = new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.OK };
            var operation = new Operation("Get.SAMPLE", typeof(string), context.Request.Url.AbsoluteUri, null, "GET", new List<OperationParameter>());

            var responseHandlers = this.OperationPipeline.ExecuteMessageHandlers(new MessageHandlerContext(request, OperationPipeline.ServiceType, operation), this.Configuration);
            bool isSuccessful = true;
            IMediaTypeFormatter responseMediaTypeFormatter = Pipeline.GetResponseMediaTypeFormatter(new HttpRequestMessage(this.RequestContext), this.Configuration);
            if (responseMediaTypeFormatter == null || !responseMediaTypeFormatter.CanSerialize(this.ReturnType))
            {
                context.Response.StatusCode = (int)System.Net.HttpStatusCode.UnsupportedMediaType;
                context.Response.End();
                return;
            }

            bool hasResponseType = true;

            Type targetType = ReturnType;
            try
            {
                if (targetType.GetInterface(typeof(IOperationResponse).FullName) != null)
                {
                    var genericParams = targetType.GetGenericArguments();
                    if (genericParams.Count() == 0)
                        hasResponseType = false;
                    else
                        targetType = genericParams.First();
                }
            }
            catch
            {
                isSuccessful = false;
            }

            if (hasResponseType && isSuccessful)
            {
                try
                {
                    var stream = responseMediaTypeFormatter.Serialize(TypeCache.GetHydratedSampleObject(targetType));
                    string responseText = System.Text.Encoding.UTF8.GetString(ReadToEnd(stream));
                    context.Response.Write(responseText);
                }
                catch(Exception ex)
                {
                    var responseMessage = new HttpResponseMessage();
                    this.Configuration.ErrorHandler.Invoke(new ErrorHandlerEventArgs(ex, this.ServiceType, new HttpRequestMessage(this.RequestContext), responseMessage));
                }

                context.Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
            }
            else if (!hasResponseType)
            {
                context.Response.StatusCode = (int)System.Net.HttpStatusCode.NoContent;
            }
            else if (!isSuccessful)
            {
                context.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
            }
                
            this.OperationPipeline.ExecuteResponseHandlers(responseHandlers, response);
            context.Response.End();
        }

        private static byte[] ReadToEnd(System.IO.Stream stream)
        {
            long originalPosition = 0;

            if (stream.CanSeek)
            {
                originalPosition = stream.Position;
                stream.Position = 0;
            }

            try
            {
                byte[] readBuffer = new byte[stream.Length];

                int totalBytesRead = 0;
                int bytesRead;

                while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
                {
                    totalBytesRead += bytesRead;

                    if (totalBytesRead == readBuffer.Length)
                    {
                        int nextByte = stream.ReadByte();
                        if (nextByte != -1)
                        {
                            byte[] temp = new byte[readBuffer.Length * 2];
                            Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                            Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                            readBuffer = temp;
                            totalBytesRead++;
                        }
                    }
                }

                byte[] buffer = readBuffer;
                if (readBuffer.Length != totalBytesRead)
                {
                    buffer = new byte[totalBytesRead];
                    Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
                }
                return buffer;
            }
            finally
            {
                if (stream.CanSeek)
                {
                    stream.Position = originalPosition;
                }
            }
        }
    }
}
